/**
 * Copyright (c) 2010-2011 julewa.com.  
 * All rights reserved. 
 * 
 * @author Huang Benhua
 * @date 2011-2-20
 * 
**/
package com.julewa.db.ibatis.loading;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import com.julewa.db.DataType;
import com.julewa.freemarker.FreemarkerBuilder;
import com.julewa.loading.SystemUtils;

public class IbatisConfiguration {
	private Properties conf;
	private Map<DataType, String> dbtypes;
	private Map<String, Pattern> typeInSql;
	private String prefix_table;
	private String prefix_column;
	private Map<String, String> ibatis_conf;
	private Map<String, String> jdbc_conf;
	private Map<String, String> pool_conf;
	private Set<String> xmls;
	private Map<String, IbatisBeanInfo> beans;
	//
	private StringBuffer sql_create = null;
	public IbatisConfiguration(Properties config, FreemarkerBuilder sqlmapbuilder){
		//include
		this.conf = config;
		this.prefix_table = conf.getProperty("prefix.table", "");
		this.prefix_column = conf.getProperty("prefix.column", "");
		this.ibatis_conf = new HashMap<String, String>();
		this.jdbc_conf = new HashMap<String, String>();
		this.pool_conf = new HashMap<String, String>();
		this.xmls = new HashSet<String>(); 
		this.beans = new HashMap<String, IbatisBeanInfo>();
		//
		loadDbtypes();
		loadAllConf();
		//
		if(sqlmapbuilder == null){
			sql_create = new StringBuffer();
			for(IbatisBeanInfo b:beans.values()){
				if(!b.getInfo().isDynamic()){
					sql_create.append(b.getTableSql());
				}
			}
			for(IbatisBeanInfo b:beans.values()){
				if(b.getInfo().isDynamic()){
					sql_create.append(b.getTableSql());
				}
			}
		}else{
			for(IbatisBeanInfo b:beans.values()){
				String path = b.getInfo().getCode() + ".xml";
				xmls.add(path);
				sqlmapbuilder.getParameters().clear();
				b.loadBeanInfo(sqlmapbuilder);
				sqlmapbuilder.build(SystemUtils.getClassesPath() + path);
			}
		};
	}
	
	public Map<String, IbatisBeanInfo> getBeans(){
		return this.beans;
	}
	
	public String findLabel(String bean, String key){
		bean = findLabel(bean + "." + key);
		if(bean == null){
			bean = findLabel(key);
		}
		if(bean == null)bean = key;
		return bean;
	}
	
	public String findLabel(String key){
		return this.conf.getProperty("label." + key);
	};
	
	public String getCreationSql(){
		if(sql_create == null)return "";
		return sql_create.toString();
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Map build(String filepath, FreemarkerBuilder configbuilder){
		configbuilder.getParameters().clear();
		configbuilder.put("settings", ibatis_conf.entrySet());
		configbuilder.put("jdbc", jdbc_conf.entrySet());
		configbuilder.put("pool", pool_conf.entrySet());
		configbuilder.put("resources", xmls);
		//
		List<String> autowireBeans = new ArrayList<String>();
		for(IbatisBeanInfo b: beans.values()){
			if(!b.getInfo().isAutowire())continue;
			autowireBeans.add(b.getInfo().getTypeClass().getName());
		}
		configbuilder.put("autowireBeans", autowireBeans);
		//
		configbuilder.build(filepath);
		Map map = new HashMap();
		//
		for(String ns:beans.keySet()){
			IbatisBeanInfo b = beans.get(ns);
			if(b == null || !b.isValid())continue;
			map.put(b.getInfo().getTypeClass(), ns);
		}
		//
		return map;
	}
	
	private String getConf(String key, String dv){
		key = conf.getProperty(key);
		if(key == null || key.equals("")){
			return dv;
		}
		return key;
	}
	
	
	
	public static void main(String a[]){
		String t = "(#\\w+:)(string|str)(#)";
		System.out.println("string#aa:string#string".replaceAll(t, "$1XX$3"));
	}
	
	private void loadDbtypes(){
		dbtypes = new HashMap<DataType, String>(); 
		typeInSql = new HashMap<String, Pattern>();
		putDbType(DataType.STRING,   "VARCHAR");
		putDbType(DataType.INT,  	 "INTEGER");
		putDbType(DataType.LONG,     "BIGINT");
		putDbType(DataType.CHAR,	 "SMALLINT");
		putDbType(DataType.FLOAT,    "FLOAT");
		putDbType(DataType.DOUBLE,   "DOUBLE");
		putDbType(DataType.BOOLEAN,  "SMALLINT");
		putDbType(DataType.BYTE,     "SMALLINT");
	}
	
	private void putDbType(DataType type, String db){
		String t = getConf("type." + type.getLabel(), db);
		dbtypes.put(type, t);
		//"(#\\w+:)string(#)"
		db += "|" + type.name() + "|" + type.getLabel();
		db += "|" + type.getObjectClass().getCanonicalName();
		//
		Pattern p = Pattern.compile("(#\\w+:)(" + db + ")(#)");
		typeInSql.put("$1" + t + "$3", p);
	}
	
	public String translateSql(String sql){
		if(sql == null)return "";
		Iterator<Entry<String, Pattern>> it = typeInSql.entrySet().iterator();
		Entry<String, Pattern> entry;
		while(it.hasNext()){
			entry = it.next();
			sql = entry.getValue().matcher(sql).replaceAll(entry.getKey());
		}
		return sql;
	}
	public String getDbPrefix(){
		return this.prefix_table;
	}
	
	public String getColPrefix(){
		return this.prefix_column;
	}
	
	public String getDbTypeOf(DataType t){
		return dbtypes.get(t);
	}
	
	@SuppressWarnings("unchecked")
	private void loadAllConf(){
		@SuppressWarnings("rawtypes")
		Set<String> keys = (Set<String>)((Set)conf.keySet());
		for(String k:keys){
			if(k.startsWith("ibatis.")){
				this.ibatis_conf.put(k.substring("ibatis.".length()), conf.getProperty(k));
				continue;
			}
			if(k.startsWith("jdbc.")){
				this.jdbc_conf.put(k.substring("jdbc.".length()), conf.getProperty(k));
				continue;
			}
			if(k.startsWith("pool.")){
				this.pool_conf.put(k.substring("pool.".length()), conf.getProperty(k));
				continue;
			}
			//
			if(k.startsWith("resource.")){
				loadResource(k.substring("resource.".length()), conf.getProperty(k, ""));
				continue;
			}
			if(k.startsWith("bean.")){
				loadBean(k.substring("bean.".length()), conf.getProperty(k, ""));
				continue;
			}
		}
	}
	
	private void loadResource(String key, String value){
		key = key.replace('.', '/') + "/";
		String v[] = value.split(",");
		for(String t:v){
			if(t == null)continue;
			t = t.trim();
			if(t.equals(""))continue;
			this.xmls.add(key + t);
		}
	}
	
	public String getTablePrefix(){
		return this.prefix_table;
	}
	
	private IbatisBeanInfo _get_bean_(String code){
		IbatisBeanInfo bean = beans.get(code);
		if(bean != null)return bean;
		String clazz = "bean." + code + ".class";
		clazz = conf.getProperty(clazz, "");
		if(StringUtils.isBlank(clazz))return null;
		bean = new IbatisBeanInfo(this, code, clazz);
		beans.put(code, bean);
		return bean;
	}

	private static int LEN_INCLUDE = "include.".length();
	private static int LEN_MAP = "map.".length();
	private void loadBean(String key, String value){
		int idx = key.indexOf('.');
		IbatisBeanInfo bean = _get_bean_(key.substring(0, idx));
		if(bean == null)return;
		key = key.substring(idx + 1);
		if("class".equals(key))return;
		
		if(key.startsWith("include.")){
			bean.loadInclude(key.substring(LEN_INCLUDE),value);
		}else if(key.startsWith("map.")){
			//ignore now
			String dbtype = null;
			//dbtypes.get(value)
			bean.loadMap(key.substring(LEN_MAP), value, dbtype, prefix_column);
		}else{
			bean.loadOperator(key, value);
		};
	}
	
}
